//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
/*-------------------------------------------------------------------------
 * Filename:      serial.c
 * Version:       $Id: serial.c,v 1.1.2.2 2007/08/02 05:26:09 zhulin Exp $
 * Copyright:     Copyright (C) 1999, Erik Mouw
 * Author:        Erik Mouw <J.A.K.Mouw@its.tudelft.nl>
 * Description:   Serial utilities for blob
 * Created at:    Tue Aug 24 20:25:00 1999
 * Modified by:   Erik Mouw <J.A.K.Mouw@its.tudelft.nl>
 * Modified at:   Mon Oct  4 20:11:14 1999
 *-----------------------------------------------------------------------*/
/*
 * serial.c: Serial utilities for blob
 *
 * Copyright (C) 1999  Erik Mouw (J.A.K.Mouw@its.tudelft.nl)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "config.h"
#include "bulverde.h"
#include "serial.h"
#include "time.h"

/* number of nibbles in a word */
#define NIBBLES_PER_WORD (8)
#define NIBBLES_PER_HALFWORD (4)

/*
 * Initialise the serial port with the given baudrate. The settings
 * are always 8 data bits, no parity, 1 stop bit, no start bits.
 *
 */
void SerialInit(eBauds baudrate)
{
    unsigned int gafr1_L;

    /* Initialize alternative function register related to STUART */
    gafr1_L = __REG(GPIO_BASE_PHYSICAL+GAFR1_L_OFFSET);
    /* gafr1_L &= ~(GPIO_46_AF3 | GPIO_47_AF3_PWMOUT3); */
    /* gafr1_L |= (GPIO_46_AF2_STDRXD_PWMOUT2 | GPIO_47_AF1_STDTXD); */
    gafr1_L = 0x69900e12;
    __REG(GPIO_BASE_PHYSICAL+GAFR1_L_OFFSET) = gafr1_L;

#ifdef ENABLE_STUART
    __REG(CLK_BASE_PHYSICAL+CKEN_OFFSET) |= CKEN5_STUART;

    __REG(STUART_BASE_PHYSICAL+ST_LCR_OFFSET) = 0;
    __REG(STUART_BASE_PHYSICAL+ST_IER_OFFSET) = 0;  /* Disable for now */
    __REG(STUART_BASE_PHYSICAL+ST_FCR_OFFSET) = 0;

//  __REG(STUART_BASE_PHYSICAL+ST_MCR_OFFSET) = 0;
//  __REG(STUART_BASE_PHYSICAL+ST_ISR_OFFSET) = 0;

    /* set baud rate */
    __REG(STUART_BASE_PHYSICAL+ST_LCR_OFFSET) = LCR_WLS0 | LCR_WLS1 | LCR_DLAB;
    __REG(STUART_BASE_PHYSICAL+ST_DLL_OFFSET) = baudrate & 0xff;
    __REG(STUART_BASE_PHYSICAL+ST_DLH_OFFSET) = (baudrate >> 8) & 0xff;
    __REG(STUART_BASE_PHYSICAL+ST_LCR_OFFSET) = LCR_WLS0 | LCR_WLS1;

#if 0 // tium
    __REG(STUART_BASE_PHYSICAL+ST_FCR_OFFSET) = 0x00;  /* No fifos enabled */
#else
    __REG(STUART_BASE_PHYSICAL+ST_FCR_OFFSET) = 0x07; /* enable FIFO */
#endif

    __REG(STUART_BASE_PHYSICAL+ST_IER_OFFSET) = IER_UUE; /* Enable STUART */
#endif

/*  /\* gafr1_L = __REG(GPIO_BASE_PHYSICAL+GAFR1_L_OFFSET); *\/ */
/*  gafr1_L = 0x999aa052; */
/*  /\* gafr1_L &= ~(GPIO_46_AF3 | GPIO_47_AF3_PWMOUT3); *\/ */
/*  /\* gafr1_L |= (GPIO_46_AF2_STDRXD_PWMOUT2 | GPIO_47_AF1_STDTXD); *\/ */
/*  __REG(GPIO_BASE_PHYSICAL+GAFR1_L_OFFSET) = gafr1_L; */

/*  __REG(GPIO_BASE_PHYSICAL+GAFR0_U_OFFSET) = 0x59e5404e; */
/*  __REG(GPIO_BASE_PHYSICAL+GPLR0_OFFSET)   = 0x20000; */
/*  __REG(GPIO_BASE_PHYSICAL+GPLR2_OFFSET)   = 0x20000; */
/*  __REG(GPIO_BASE_PHYSICAL+GPSR0_OFFSET)   = 0x28800; */

/* #ifdef ENABLE_BTUART */
/*  __REG(CLK_BASE_PHYSICAL+CKEN_OFFSET) |= CKEN7_BTUART; */

/*  __REG(BTUART_BASE_PHYSICAL+BT_LCR_OFFSET) = 0; */
/*  __REG(BTUART_BASE_PHYSICAL+BT_IER_OFFSET) = 0;  /\* Disable for now *\/ */
/*  __REG(BTUART_BASE_PHYSICAL+BT_FCR_OFFSET) = 0; */

/* //   __REG(STUART_BASE_PHYSICAL+ST_MCR_OFFSET) = 0; */
/* //   __REG(STUART_BASE_PHYSICAL+ST_ISR_OFFSET) = 0; */

/*  /\* set baud rate *\/ */
/*  __REG(BTUART_BASE_PHYSICAL+BT_LCR_OFFSET) = LCR_WLS0 | LCR_WLS1 | LCR_DLAB; */
/*  __REG(BTUART_BASE_PHYSICAL+BT_DLL_OFFSET) = baudrate & 0xff; */
/*  __REG(BTUART_BASE_PHYSICAL+BT_DLH_OFFSET) = (baudrate >> 8) & 0xff; */
/*  __REG(BTUART_BASE_PHYSICAL+BT_LCR_OFFSET) = LCR_WLS0 | LCR_WLS1; */

/* #if 0 // tium */
/*  __REG(BTUART_BASE_PHYSICAL+BT_FCR_OFFSET) = 0x00;  /\* No fifos enabled *\/ */
/* #else */
/*  __REG(BTUART_BASE_PHYSICAL+BT_FCR_OFFSET) = 0x07; /\* enable FIFO *\/ */
/* #endif */

/*  __REG(BTUART_BASE_PHYSICAL+BT_IER_OFFSET) = IER_UUE; /\* Enable STUART *\/ */
/* #endif */

/*  __REG(BTUART_BASE_PHYSICAL+BT_FCR_OFFSET) = 0x07; /\* enable FIFO *\/ */
}

/*
 * Output a single byte to the serial port.
 */
void SerialOutputByte(const unsigned char c)
{
  /* If \n, also do \r */
    if (c == '\n')
        SerialOutputByte('\r');

#ifdef ENABLE_STUART
    /* wait for room in the tx FIFO on STUART */
    while(( __REG(STUART_BASE_PHYSICAL+ST_LSR_OFFSET) & LSR_TDRQ) == 0) ;
    __REG(STUART_BASE_PHYSICAL+ST_THR_OFFSET) = c & 0xff;
#endif

/* #ifdef ENABLE_BTUART */
/*   /\* wait for room in the tx FIFO on STUART *\/ */
/*   while(( __REG(BTUART_BASE_PHYSICAL+BT_LSR_OFFSET) & LSR_TDRQ) == 0) ; */
/*   __REG(BTUART_BASE_PHYSICAL+BT_THR_OFFSET) = c & 0xff; */
/* #endif */
}

/*
 * Output a single byte to the serial port.
 */
void SerialOutputRawByte(const unsigned char c)
{
#ifdef ENABLE_STUART
    /* wait for room in the tx FIFO on STUART */
    while(( __REG(STUART_BASE_PHYSICAL+ST_LSR_OFFSET) & LSR_TDRQ) == 0) ;
    __REG(STUART_BASE_PHYSICAL+ST_THR_OFFSET) = c & 0xff;
#endif

/* #ifdef ENABLE_BTUART */
/*   /\* wait for room in the tx FIFO on STUART *\/ */
/*   while(( __REG(BTUART_BASE_PHYSICAL+BT_LSR_OFFSET) & LSR_TDRQ) == 0) ; */
/*   __REG(BTUART_BASE_PHYSICAL+BT_THR_OFFSET) = c & 0xff; */
/* #endif */
}

void SerialOutputByteInHex(const char c)
{
    char d;

    d = (char)((c >> 4) & 0x0f);
    if (d > 9)
        d += ('A' - 10);
    else
        d += '0';
    SerialOutputByte(d);

    d = (char)(c & 0x0f);
    if (d > 9)
        d += ('A' - 10);
    else
        d += '0';
    SerialOutputByte(d);
}

/*
 * Write a null terminated string to the serial port.
 */
void SerialOutputString(const char *s)
{
    while (*s != 0) {
        SerialOutputByte(*s++);
    }
}

/*
 * Write the argument of the function in hexadecimal to the serial
 * port. If you want "0x" in front of it, you'll have to add it
 * yourself.
 */
void SerialOutputHex(const u32 h)
{
    char c;
    int i;

    for (i = NIBBLES_PER_WORD - 1; i >= 0; i--) {
        c = (char)((h >> (i * 4)) & 0x0f);

        if (c > 9)
            c += ('A' - 10);
        else
            c += '0';

        SerialOutputByte(c);
    }
}

void SerialOutputSHex(const u32 h)
{
    char c;
    int i;

    for (i = NIBBLES_PER_HALFWORD - 1; i >= 0; i--) {
        c = (char)((h >> (i * 4)) & 0x0f);

        if (c > 9)
            c += ('A' - 10);
        else
            c += '0';

        SerialOutputByte(c);
    }
}

/*
 * Write the argument of the function in decimal to the serial port.
 * We just assume that each argument is positive (i.e. unsigned).
 */
void SerialOutputDec(const u32 d)
{
    int leading_zero = 1;
    u32 divisor, result, remainder;

    remainder = d;

    for (divisor = 1000000000;
        divisor > 0;
        divisor /= 10) {
        result = remainder / divisor;
        remainder %= divisor;

        if (result != 0 || divisor == 1)
            leading_zero = 0;

        if (leading_zero == 0)
            SerialOutputByte((char)(result) + '0');
    }
}

/*
 * Read a single byte from the serial port. Returns 1 on success, 0
 * otherwise. When the function is succesfull, the character read is
 * written into its argument c.
 */
int SerialInputStr(char *buf, int count)
{
    int i;
#ifdef ENABLE_STUART
    for (i = 0; i < count; i++) {
        while ((__REG(STUART_BASE_PHYSICAL + ST_LSR_OFFSET) & LSR_DR) == 0)
            msleep(1);
        buf[i] = __REG(STUART_BASE_PHYSICAL+ST_RBR_OFFSET) & 0xff;
    }
    return(1);
#endif
} /* SerialInputByte */

/*
 * Write a block of data to the serial port. Similar to
 * SerialOutputString(), but this function just writes the number of
 * characters indicated by bufsize and doesn't look at termination
 * characters.
 */
void SerialOutputBlock(const char *buf, int bufsize)
{
    while (bufsize--)
        SerialOutputByte(*buf++);
}

/*
 * Read a single byte from the serial port. Returns 1 on success, 0
 * otherwise. When the function is succesfull, the character read is
 * written into its argument c.
 */
int SerialInputByte(char *c)
{
    unsigned int ch;

#ifdef ENABLE_STUART
    if ((__REG(STUART_BASE_PHYSICAL + ST_LSR_OFFSET) & LSR_DR) != 0) {
        ch = __REG(STUART_BASE_PHYSICAL+ST_LSR_OFFSET);

        if (ch & LSR_PE)
            SerialOutputByte('@');
        else if (ch & LSR_FE)
            SerialOutputByte('#');
        else if (ch & LSR_OE)
            SerialOutputByte('$');

        /* We currently only care about framing and parity errors */
        if ((ch & (LSR_PE | LSR_FE)) != 0) {
            return SerialInputByte(c);
        }
        else {
            *c = __REG(STUART_BASE_PHYSICAL+ST_RBR_OFFSET) & 0xff;
            return(1);
        }
    }
    else { /* no bit ready */
        return(0);
    }
#endif

/* #ifdef ENABLE_BTUART */
/*  if (( __REG(BTUART_BASE_PHYSICAL+BT_LSR_OFFSET) & LSR_DR) != 0) { */
/*      ch = __REG(BTUART_BASE_PHYSICAL+BT_LSR_OFFSET); */

/*      if (ch & LSR_PE) */
/*          SerialOutputByte('@'); */
/*      else if (ch & LSR_FE) */
/*          SerialOutputByte('#'); */
/*      else if (ch & LSR_OE) */
/*          SerialOutputByte('$'); */

/*      /\* We currently only care about framing and parity errors *\/ */
/*      if ( (ch & (LSR_PE | LSR_FE) ) != 0) { */
/*          return SerialInputByte(c); */
/*      } else { */
/*          *c = __REG(BTUART_BASE_PHYSICAL+BT_RBR_OFFSET) & 0xff; */
/*          return(1); */
/*      } */
/*  }else { /\* no bit ready *\/ */
/*      return(0); */
/*  } */
/* #endif */
} /* SerialInputByte */

/*
 * read a string with maximum length len from the serial port
 * using a timeout of timeout seconds
 *
 * len is the length of array s _including_ the trailing zero,
 * the function returns the number of bytes read _excluding_
 * the trailing zero
 */
int  SerialInputString(char *s, const int len, const int timeout)
{
    u32 startTime, currentTime;
    char c;
    int i;
    int numRead;
    int skipNewline = 1;
    int maxRead = len - 1;

    startTime = TimerGetTime();

    for (numRead = 0, i = 0; numRead < maxRead;) {
        /* try to get a byte from the serial port */
        while (!SerialInputByte(&c)) {
            currentTime = TimerGetTime();

            /* check timeout value */
            if ((currentTime - startTime) > timeout) {
                /* timeout */
                s[i++] = '\0';
                return(numRead);
            }
        }
        startTime = TimerGetTime();
        /* eat newline characters at start of string */
        if ((skipNewline == 1) && (c != '\r') && (c != '\n'))
            skipNewline = 0;

        if (skipNewline == 0) {
            if ((c == '\r') || (c == '\n')) {
                s[i++] = '\0';
                return(numRead);
            }
            else {
                s[i++] = c;
                numRead++;
            }
        }
    }

    return(numRead);
}

/*
 * SerialInputBlock(): almost the same as SerialInputString(), but
 * this one just reads a block of characters without looking at
 * special characters.
 */
int  SerialInputBlock(char *buf, int bufsize, const int timeout)
{
    u32 startTime, currentTime;
    char c;
    int i;
    int numRead;
    int maxRead = bufsize;

    startTime = TimerGetTime();

    for (numRead = 0, i = 0; numRead < maxRead;) {
      /* try to get a byte from the serial port */
        while (!SerialInputByte(&c)) {
            currentTime = TimerGetTime();

            /* check timeout value */
            if ((currentTime - startTime) > timeout) {
                /* timeout! */
                return(numRead);
            }
        }
//      startTime = TimerGetTime();
        buf[i++] = c;
        numRead ++;
    }

    return(numRead);
}

int SerialInputBlock_msec(char *buf, int bufsize, const int timeout_msec)
{
    u32 startTime, currentTime;
    char c;
    int i;
    int numRead;
    int maxRead = bufsize;

    startTime = jiffies;

    for (numRead = 0, i = 0; numRead < maxRead;) {
      /* try to get a byte from the serial port */
        while (!SerialInputByte(&c)) {

            currentTime = jiffies;
                        /* check timeout value */
            if ((currentTime - startTime) > timeout_msec /* * 3250000 */) {
                /* timeout! */
                return(numRead);
            }
        }
//      startTime = TimerGetTime();
        buf[i++] = c;
        numRead ++;
    }

    return(numRead);
}

void SerialInputFlush(void)
{
    volatile unsigned int ch;
    while ((__REG(STUART_BASE_PHYSICAL + ST_LSR_OFFSET) & LSR_DR) != 0) {
        if (__REG(STUART_BASE_PHYSICAL + ST_LSR_OFFSET) & (LSR_FE | LSR_PE | LSR_OE))
            return;
        ch = __REG(STUART_BASE_PHYSICAL+ST_RBR_OFFSET);
    }
    return;
}

int SerialInputDWordWithTimeout()
{
    int nRet;

    if (SerialInputBlock_msec((char *)&nRet, 4, 10) < 4) {
        return 0;
    }
    else {
        return nRet;
    }
}
